﻿using Microsoft.Dism;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;


#pragma warning disable CA1416

#if RELEASE
#error 软件内部版本类型“Release”已由“Beta”和“Stable”继承，请指定Target为“Debug”、“Beta”或“Stable”
#endif

namespace AZPEP_SysRi_Tool
{
    public partial class MainWindow : Form
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void MainWindow_Load(object sender, EventArgs e)
        {
            //启动日志服务，启动失败，报错退出
            if (!LogProtocol.StartLogServices("AZPEP SysRi Tool 日志服务 - 主程序"))
            {
                MyError.ShowError(
                    ErrCode.ErrorCode.__MAINPROG_INIT_START_LOG_SERVICES_FAIL,
                    $"错误代码：{Win32API.GetLastError()}，请前往“https://learn.microsoft.com/zh-cn/windows/win32/debug/system-error-codes”查询错误信息",
                    false
                    );
                MyError.EmergencyExitEx();
            }

            LogProtocol.WriteLog.Info("已成功开启日志服务！\n");
            LogProtocol.WriteLog.Info("--- 开始初始化程序 ---");

            //检查电脑是否为UEFI启动
            LogProtocol.WriteLog.Info("[INIT]   第1步，共3步   检查电脑启动方式");
            if (!IsUefi())
            {
                MyError.ShowError(
                    ErrCode.ErrorCode.__MAINPROG_INIT_NO_UEFI,
                    $"您的电脑不支持UEFI启动，或者您不是以UEFI方式启动Windows。"
                    );
                MyError.EmergencyExitEx();
            }
            LogProtocol.WriteLog.Info("[INIT]   已检测到Windows为UEFI启动！\n");

            //获取计算机上所有的ESP分区
            LogProtocol.WriteLog.Info("[INIT]   第2步，共3步   获取电脑上所有的EFI分区");

            string EfiPartitionLetter = ""; // 存放EFI分区盘符的string，本来应该用char[]代替，可无奈CLI总是搞错。
            try
            {
                //先卸载，再挂载
                EfiPartitionLetter = ESPCS.ESP.EspCs_UnmountEfiPartition();
                EfiPartitionLetter = ESPCS.ESP.EspCs_MountEfiPartition();
            }
            catch (Exception err)
            {
                ESPCS.ESP.ErrorCode Code;
                Code = ESPCS.ESP.EspCs_GetLastError();  //获取错误信息

                if (Code == ESPCS.ESP.ErrorCode.ESPCS_ERROR_UNKNOWN)
                {
                    MyError.ShowError(
                        ErrCode.ErrorCode.__MAINPROG_INIT_GET_ESP_FAIL,
                        $"错误代码：{ESPCS.ESP.ErrorCode.ESPCS_ERROR_UNKNOWN}。Win32错误代码：{Win32API.GetLastError()}。\n请前往“https://learn.microsoft.com/zh-cn/windows/win32/debug/system-error-codes”查询错误信息",
                        err: err
                        );
                    MyError.EmergencyExitEx();
                }

                MyError.ShowError(
                    ErrCode.ErrorCode.__MAINPROG_INIT_GET_ESP_FAIL,
                    $"错误代码：{Code}。Win32错误代码：{Win32API.GetLastError()}",
                    err: err
                    );
                MyError.EmergencyExitEx();
            }

            //如果没有EFI分区，报错
            if (EfiPartitionLetter == "")
            {
                MyError.ShowError(
                    ErrCode.ErrorCode.__MAINPROG_INIT_GET_ESP_FAIL,
                    "未找到EFI分区，请确认你的磁盘上有GPT磁盘且有EFI分区"
                    );
                MyError.EmergencyExitEx();
            }

            //处理磁盘
            ParsePartition(EfiPartitionLetter);

            //处理界面
            LogProtocol.WriteLog.Info("[INIT]   第3步，共3步   界面处理\n");

            try
            {
                //
                //  comboBox_AfterInstall
                //
                comboBox_AfterInstall.Items.Add(label_Text_AfterSetting_None.Text);     //安装后操作- 无操作
                comboBox_AfterInstall.Items.Add(label_Text_AfterSetting_Reboot.Text);   //安装后操作 - 15秒后重启
                comboBox_AfterInstall.Items.Add(label_Text_AfterSetting_Shutdown.Text); //安装后操作 - 15秒后关机
                comboBox_AfterInstall.SelectedIndex = 1;                                //安装后操作 - 默认值：15秒后重启

                //
                // comboBox_SelectEfiPartition
                //
                // 遍历添加选项
                foreach (GlobalVariable.SimpleEfiPartitionInformation SEPI in GlobalVariable.EfiInfoList)
                {
#if DEBUG
                    comboBox_SelectEfiPartition.Items.Add($"(hd{SEPI.DiskNumber},{SEPI.PartitionNumber})   {SEPI.PartitionPath}   {SEPI.Letter}:\\");
#else
                    comboBox_SelectEfiPartition.Items.Add($"(hd{SEPI.DiskNumber},{SEPI.PartitionNumber})   {SEPI.Letter}:\\");
#endif
                }
                comboBox_SelectEfiPartition.SelectedIndex = 0;                          //选择ESP分区  默认项：第一项

                label_SystemPartitionInfo.Text = label_SystemPartitionInfo.Text.FormatEx($"{GlobalVariable.SystemPartitionLetter}", $"{GlobalVariable.SystemPartitionTotalSize}");

                //初始化窗体标题
                Version version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
                this.Text = this.Text.FormatEx(version.ToString());

#if DEBUG
                this.Text += "  DEBUG";
#endif
#if BETA
                this.Text += "  BETA";
#endif
#if STABLE
                this.Text += "  Release";
#endif

            }
            catch (Exception err)
            {
                MyError.ShowError(
                    ErrCode.ErrorCode.__MAINPROG_INIT_SETTING_MAINWINDOW_FAIL,
                    $"{err.Message}",
                    err: err
                    );
                MyError.EmergencyExitEx();
            }
            LogProtocol.WriteLog.Info("[INIT]   初始化完成！");
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern UInt32 GetFirmwareEnvironmentVariableA([MarshalAs(UnmanagedType.LPWStr)] string lpName, [MarshalAs(UnmanagedType.LPWStr)] string lpGuid, IntPtr pBuffer, UInt32 nSize);
        private static bool IsUefi()
        {
            //ERROR_INVALID_FUNCTION 1(0x1)
            //ERROR_NOACCESS 998(0x3E6)
            GetFirmwareEnvironmentVariableA("", "{00000000-0000-0000-0000-000000000000}", IntPtr.Zero, 0);
            if (Marshal.GetLastWin32Error() == 0x1)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        /// <summary>
        /// 处理EFI磁盘信息
        /// </summary>
        /// <param name="Letter">盘符的string，由</param>
        private void ParsePartition(string Letter)
        {
            /*
             TODO:
            选择的磁盘满足两条：
                1.磁盘已准备
                2.固定磁盘

            赋值
             */

            DriveInfo[] DIL = DriveInfo.GetDrives();

            List<char> LetterList = new List<char>();

            foreach (DriveInfo d in DIL)
            {
                if (Letter.ToLower().IndexOf(d.Name.ToLower()[0]) != -1 && d.IsReady && d.DriveType == DriveType.Fixed)
                {
                    //满足上面两条，装载
                    LogProtocol.WriteLog.Info($"[INIT][ESP]   已识别EFI分区\"{d.Name[0]}:\\\"");
                    LetterList.Add(d.Name[0]);
                }
                else
                {
                    if (!d.IsReady)
                    {
                        LogProtocol.WriteLog.Warning($"[INIT][ESP]   EFI分区\"{d.Name[0]}:\\\"未准备！");
                    }
                    if (d.DriveType != DriveType.Fixed)
                    {
                        LogProtocol.WriteLog.Warning($"[INIT][ESP]   EFI分区\"{d.Name[0]}:\\\"：不是一个固定磁盘！");
                    }
                }
            }

            if (LetterList.Count == 0)
            {
                MyError.ShowError(
                    ErrCode.ErrorCode.__MAINPROG_INIT_NO_ESP,
                    "固定磁盘上没有准备好的EFI分区！"
                    );
                MyError.EmergencyExitEx();
            }

            foreach (char c in LetterList)
            {
                GlobalVariable.SimpleEfiPartitionInformation SEPI = new GlobalVariable.SimpleEfiPartitionInformation();

                //赋值
                SEPI.Letter = c;
                SEPI.DiskNumber = ESPCS.ESP.EspCS_Partition_GetHardDiskNumberByLetter((byte)c);
                SEPI.PartitionNumber = ESPCS.ESP.EspCS_Partition_GetPartitionNumberByLetter((byte)c);
                SEPI.PartitionPath = $"\\Harddisk{SEPI.DiskNumber}\\Partition{SEPI.PartitionNumber}";

                LogProtocol.WriteLog.Info($"[INIT][ESP]   成功获取磁盘\"{c}:\\\"信息！");
                LogProtocol.WriteLog.Debug($"[INIT][ESP]   以下是磁盘\"{c}:\\\"详细信息：");
                LogProtocol.WriteLog.Debug("[INIT][ESP]   ----------");
                LogProtocol.WriteLog.Debug($"[INIT][ESP]   盘符：{SEPI.Letter}");
                LogProtocol.WriteLog.Debug($"[INIT][ESP]   硬盘号：{SEPI.DiskNumber}");
                LogProtocol.WriteLog.Debug($"[INIT][ESP]   分区号：{SEPI.PartitionNumber}");
                LogProtocol.WriteLog.Debug($"[INIT][ESP]   磁盘UNC分区路径：{SEPI.PartitionPath}\n");

                GlobalVariable.EfiInfoList.Add(SEPI);
            }
        }

        private void button_ViewImageFile_Click(object sender, EventArgs e)
        {
            //打开对话框
            DialogResult Status = openFileDialog_OpenImageFile.ShowDialog();

            //判断状态，未打开成功，返回
            if (Status != DialogResult.OK)
            {
                return;
            }

            //否则，开始解析文件

            //尝试打开文件，打开成功，继续，打开失败，返回

            //尝试使用dismapi打开文件

            DismApi.Initialize(DismLogLevel.LogErrorsWarningsInfo); //打开DismAPI服务
            List<DismImageInfo> DIIL = new List<DismImageInfo>();

            try
            {
                DismImageInfoCollection ImageInfoCollection = DismApi.GetImageInfo(openFileDialog_OpenImageFile.FileName);    //尝试获取所有映像信息

                LogProtocol.WriteLog.Info($"共从用户选择的映像文件“{openFileDialog_OpenImageFile.FileName}”中找到 {ImageInfoCollection.Count} 个映像");
                foreach (DismImageInfo DII in ImageInfoCollection)
                {
#if DEBUG
                    LogProtocol.WriteLog.Debug("====================");
                    LogProtocol.WriteLog.Debug($"索引：        {DII.ImageIndex}");
                    LogProtocol.WriteLog.Debug("");
                    LogProtocol.WriteLog.Debug($"映像名称：    {DII.ImageName}");
                    LogProtocol.WriteLog.Debug($"映像描述：    {DII.ImageDescription}");
                    LogProtocol.WriteLog.Debug($"映像大小：    {DII.ImageSize} 字节");
                    LogProtocol.WriteLog.Debug($"内核版本号：  {DII.ProductVersion}");
                    LogProtocol.WriteLog.Debug("====================");
#endif
                    DIIL.Add(DII);
                }
            }
            catch (Exception err)
            {
                //报错，返回
                MyError.ShowError(
                    ErrCode.ErrorCode.__MAINPROG_RUN_GET_IMAGE_INFO_FAIL,
                    $"{err.Message}",
                    err: err
                    );
                return;
            }

            //到这一步基本上能证明映像可以打开了。所以我们开始设置界面

            //清除信息
            GlobalVariable.DismImageInfoList.Clear();
            GlobalVariable.ImageSize = 0;
            comboBox_SelectImage.Enabled = false;
            comboBox_SelectImage.SelectedIndex = -1;
            comboBox_SelectImage.Items.Clear();

            //设置信息
            GlobalVariable.DismImageInfoList = DIIL;
            foreach (DismImageInfo DI in DIIL)
            {
                comboBox_SelectImage.Items.Add(label_Text_ImageInfo.Text.FormatEx($"{DI.ImageIndex}", $"{DI.ImageName}"));
            }

            textBox_ImageFilePath.Text = openFileDialog_OpenImageFile.FileName;
            GlobalVariable.ImageFilePath = openFileDialog_OpenImageFile.FileName;

            comboBox_SelectImage.SelectedIndex = 0;
            comboBox_SelectImage.Enabled = true;

        }

        private void comboBox_SelectImage_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (comboBox_SelectImage.SelectedIndex == -1)
            {
                //未选中，隐藏这些选项
                label_ImageInfo_SystemType.Visible = false;
                label_ImageInfo_Description.Visible = false;
                label_ImageInfo_TotalSize.Visible = false;
                label_Text_DriveStatus.Visible = false;

                button_Install.Enabled = false;

                return;
            }
            button_Install.Enabled = false;
            //获取映像信息
            DismImageInfo DI = GlobalVariable.DismImageInfoList[comboBox_SelectImage.SelectedIndex];

            label_ImageInfo_SystemType.Text = label_ImageInfo_SystemType_COPY.Text.FormatEx($"{DI.ProductVersion}"); //版本信息
            label_ImageInfo_Description.Text = label_ImageInfo_Description_COPY.Text.FormatEx($"{DI.ImageDescription}"); //描述
            label_ImageInfo_TotalSize.Text = label_ImageInfo_TotalSize_COPY.Text.FormatEx($"{DI.ImageSize}");        //大小

            GlobalVariable.ImageSize = DI.ImageSize;

            //判断磁盘总空间是否有足够的空间安装系统

            if (GlobalVariable.SystemPartitionTotalSize >= GlobalVariable.ImageSize)
            {
                label_Text_DriveStatus.ForeColor = Color.DarkGreen;
                label_Text_DriveStatus.Text = label_Text_Drive_Can.Text.FormatEx($"{GlobalVariable.SystemPartitionLetter}");
                button_Install.Enabled = true;
            }
            else if (GlobalVariable.SystemPartitionTotalSize < GlobalVariable.ImageSize)
            {
                decimal sub = (long)GlobalVariable.ImageSize - (long)GlobalVariable.SystemPartitionTotalSize;
                label_Text_DriveStatus.ForeColor = Color.DarkRed;
                label_Text_DriveStatus.Text = label_Text_Drive_Cant.Text.FormatEx($"{GlobalVariable.SystemPartitionLetter}", $"{sub}");
                button_Install.Enabled = false;
            }
            else
            {
                //报错，返回
                MyError.ShowError(
                    ErrCode.ErrorCode.WHAT_THE_FXXK,
                    $"\nGlobalVariable.ImageSize={GlobalVariable.ImageSize}\nGlobalVariable.SystemPartitionTotalSize={GlobalVariable.SystemPartitionTotalSize}\nWTF?\n比个大小“>”“<”“=”都比完了还能跳到这行代码？\n总之应该是编译器的锅，所以程序将要退出。"
                    );
                MyError.EmergencyExitEx(2);
            }

            GlobalVariable.SelectedIndex = comboBox_SelectImage.SelectedIndex + 1;

            label_ImageInfo_SystemType.Visible = true;
            label_ImageInfo_Description.Visible = true;
            label_ImageInfo_TotalSize.Visible = true;
            label_Text_DriveStatus.Visible = true;

            button_Install.Enabled = true;
        }

        private void button_Install_Click(object sender, EventArgs e)
        {
            DialogResult Return = MessageBox.Show(
                label_Text_Question_Install.Text.FormatEx(GlobalVariable.SystemPartitionLetter.ToString()),  //你确定要开始配置重装环境吗？
                "",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Question
                );
            if (Return == DialogResult.No) return;

            ErrCode.ErrorCode CallBackErrorCode = ErrCode.ErrorCode.SUCCESS;

            //启动制作PE盘
            CreatePE.MakePEStartupEntry(
                $"{GlobalVariable.MainProgramDir}Data\\WinPE\\boot.wim", $"{GlobalVariable.MainProgramDir}Data\\WinPE\\boot.sdi",
                GlobalVariable.ImageFilePath, GlobalVariable.SelectedIndex,
                $"{GlobalVariable.EfiInfoList[comboBox_SelectEfiPartition.SelectedIndex].Letter}", $"{GlobalVariable.SystemPartitionLetter}",
                (long)GlobalVariable.ImageSize / 1024,
                checkBox_DisabledWindowsDefender.Checked, checkBox_DisabledWindowsUpdate.Checked, checkBox_SkipMicrosoftLogin.Checked,
                out CallBackErrorCode
                );

            //创建失败，返回
            if (CallBackErrorCode != ErrCode.ErrorCode.SUCCESS) return;

            switch (comboBox_AfterInstall.SelectedIndex)
            {
                // 无操作
                case 0:
                    ProgramPowerControl.ThreadShowInfoMessageBox(
                        label_Text_InstallDone_None.Text // 重装环境配置完成！
                        );
                    break;

                // 15秒后重启
                case 1:
                    ProgramPowerControl.ThreadShowInfoMessageBox(
                        label_Text_InstallDone_Reboot.Text // 重装环境配置完成，系统将于15秒后自动重启，请保存并关闭重要资料。请保存并关闭重要资料，以免造成不必要的损失。
                        );
                    ProgramPowerControl.RebootInSeconds(15);
                    break;

                // 15秒后关机
                case 2:
                    ProgramPowerControl.ThreadShowInfoMessageBox(
                        label_Text_InstallDone_Shutdown.Text // 重装环境配置完成，系统将于15秒后自动关机，请保存并关闭重要资料。请保存并关闭重要资料，以免造成不必要的损失。
                        );
                    ProgramPowerControl.ShutdownInSeconds(15);
                    break;
            }
        }

        private void 实用程序ToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

        private void 傲梅分区助手技术员版ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                // 通过多进程启动程序
                // 位置：Data\UsefulProgram\PartitionAssistant\PartAssist.exe

                ProcessStartInfo processInfo = new ProcessStartInfo();
                processInfo.FileName = $"{GlobalVariable.MainProgramDir}Data\\UsefulProgram\\PartitionAssistant\\PartAssist.exe";

                Process pro = new Process();
                pro.StartInfo = processInfo;
                pro.Start();
            }
            catch (Exception err)
            {
                MyError.ShowError(
                    ErrCode.ErrorCode.__MAINPROG_RUN_RUN_PROGRAM_FAIL,
                    $"{err.Message}",
                    err: err
                    );
                return;
            }
        }

        private void booticeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                // 通过多进程启动程序
                // 位置：Data\UsefulProgram\bootice\bootice.exe

                ProcessStartInfo processInfo = new ProcessStartInfo();
                processInfo.FileName = $"{GlobalVariable.MainProgramDir}Data\\UsefulProgram\\bootice\\bootice.exe";

                Process pro = new Process();
                pro.StartInfo = processInfo;
                pro.Start();
            }
            catch (Exception err)
            {
                MyError.ShowError(
                    ErrCode.ErrorCode.__MAINPROG_RUN_RUN_PROGRAM_FAIL,
                    $"{err.Message}",
                    err: err
                    );
                return;
            }
        }

        private void 关于ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AboutBox About = new AboutBox();
            About.ShowDialog();
        }

        private void 开源许可证ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Process.Start("explorer.exe", "https://github.com/AZPEP-BiliBili/AZPEP-SysRi-Tool/blob/master/LICENSE");
        }

        private void 意见反馈ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Process.Start("explorer.exe", "https://github.com/AZPEP-BiliBili/AZPEP-SysRi-Tool/issues");
        }

        private void 检查更新ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Process.Start("explorer.exe", "https://github.com/AZPEP-BiliBili/AZPEP-SysRi-Tool/releases");
        }

        private void 退出ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            System.Environment.Exit(0);
        }
    }

    public class GlobalVariable
    {
        public struct SimpleEfiPartitionInformation
        {
            //盘符，如“C”
            public char Letter;

            /// <summary>
            /// 分区所在硬盘序号
            /// </summary>
            public short DiskNumber;

            /// <summary>
            /// 分区在磁盘中的序号
            /// </summary>
            public short PartitionNumber;

            /// <summary>
            /// 分区UNC路径
            /// </summary>
            public string PartitionPath;
        }

        /// <summary>
        /// 程序运行目录，如“Q:\xxx\xxx\”
        /// </summary>
        public static readonly string MainProgramDir = System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase;

        /// <summary>
        /// EFI分区信息表
        /// </summary>
        public static List<SimpleEfiPartitionInformation> EfiInfoList = new List<SimpleEfiPartitionInformation>();

        /// <summary>
        /// 系统分区盘符（如“C”）
        /// </summary>
        public static readonly char SystemPartitionLetter = System.Environment.GetEnvironmentVariable("SystemDrive")[0];

        /// <summary>
        /// 系统分区总大小（字节）
        /// </summary>
        public static readonly ulong SystemPartitionTotalSize = (ulong)GetHardDiskSpace($"{SystemPartitionLetter}");

        /// <summary>
        /// 映像文件路径
        /// </summary>
        public static string ImageFilePath = "?:\\???.???";

        /// <summary>
        /// 被选中的映像的索引，从1开始
        /// </summary>
        public static int SelectedIndex = -1;

        /// <summary>
        /// 映像信息
        /// </summary>
        public static List<DismImageInfo> DismImageInfoList = new List<DismImageInfo>();

        public static ulong ImageSize = 0;


        /// 获取指定驱动器的空间总大小(单位为B) 
        ///   
        ///  只需输入代表驱动器的字母即可 （大写） ，失败返回-1
        ///    
        private static long GetHardDiskSpace(string str_HardDiskName)
        {
            long totalSize = -1;
            str_HardDiskName = str_HardDiskName + ":\\";
            System.IO.DriveInfo[] drives = System.IO.DriveInfo.GetDrives();
            foreach (System.IO.DriveInfo drive in drives)
            {
                if (drive.Name == str_HardDiskName)
                {
                    totalSize = drive.TotalSize;
                }
            }
            return totalSize;
        }
    }
}
